package com.faxsun.core.order.dao;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Query;
import javax.persistence.TypedQuery;

import com.faxsun.profile.core.domain.FSReferralCustomerOrderItemXref;
import com.faxsun.profile.core.domain.FSReferralStatusEnum;
import org.broadleafcommerce.core.catalog.domain.Category;
import org.broadleafcommerce.core.catalog.domain.Product;
import org.broadleafcommerce.core.catalog.domain.ProductImpl;
import org.broadleafcommerce.core.catalog.domain.Sku;
import org.broadleafcommerce.core.order.dao.OrderItemDaoImpl;
import org.broadleafcommerce.core.order.domain.OrderItem;
import org.broadleafcommerce.core.order.domain.OrderItemAttribute;
import org.broadleafcommerce.core.order.service.type.OrderStatus;
import org.broadleafcommerce.profile.core.domain.Customer;
import org.hibernate.jpa.QueryHints;

import com.faxsun.core.order.service.type.FSInternalOrderStatus;

public class FSOrderItemDaoImpl extends OrderItemDaoImpl implements FSOrderItemDao {

    @Override
    public Long readOrderItemQuantitiesByCustomerAndCategory(Customer customer, Category category) {
        Query query = em
            .createQuery("SELECT SUM (orderItems.quantity) FROM org.broadleafcommerce.core.order.domain.OrderItem orderItems "
                + "JOIN orderItems.order o "
                + "WHERE o.customer = :customer "
                + "AND orderItems.category = :category");
        query.setParameter("customer", customer);
        query.setParameter("category", category);
        query.setHint(QueryHints.HINT_CACHEABLE, true);
        query.setHint(QueryHints.HINT_CACHE_REGION, "query.Catalog");
        return (Long) query.getSingleResult();
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<OrderItem> readOrderItemsByCustomerAndCategory(Customer customer, Category category) {
        //FIXME: 需要判断orderinternalstatus不为PAYMENT_FAIL
        Query query = em
            .createQuery("SELECT orderItems FROM org.broadleafcommerce.core.order.domain.OrderItem orderItems "
                + ", com.faxsun.core.order.domain.FSOrderImpl o " + "WHERE o.customer = :customer "
                + "AND (o.internalStatus is null "
                + "OR o.internalStatus <> :status) "
                + "AND o.id = orderItems.order.id "
                + "AND orderItems.category = :category");
        query.setParameter("customer", customer);
        // FIXME: 彩蛋 - 若提交多次而暂不支付，则可以实现多次夺宝同一物品功能。
        query.setParameter("status", FSInternalOrderStatus.PAYMENT_FAIL.getType());
        query.setParameter("category", category);
        query.setHint(QueryHints.HINT_CACHEABLE, true);
        query.setHint(QueryHints.HINT_CACHE_REGION, "query.Catalog");
        return query.getResultList();
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<OrderItem> readReFoundOrderItemsByCustomer(Customer customer) {
        Query query = em
            .createQuery("SELECT orderItems FROM org.broadleafcommerce.core.order.domain.OrderItem orderItems "
                + ", com.faxsun.core.order.domain.FSOrderImpl o " + "WHERE o.customer = :customer "
                + "AND (orderItems.status = :request OR  orderItems.status = :process "
                + "OR orderItems.status = :complete) "
                + "AND o.id = orderItems.order.id");
        query.setParameter("customer", customer);
        query.setParameter("request", FSInternalOrderStatus.REFUND_REQUEST.getType());
        query.setParameter("process", FSInternalOrderStatus.REFUND_PROCESSING.getType());
        query.setParameter("complete", FSInternalOrderStatus.REFUND_COMPLETE.getType());
        query.setHint(QueryHints.HINT_CACHEABLE, true);
        query.setHint(QueryHints.HINT_CACHE_REGION, "query.Catalog");
        return query.getResultList();
    }

    @SuppressWarnings("unchecked")
    public List<OrderItemAttribute> readAllOrderItemAttributesBySku(Sku sku) {
        Query query = em
            .createQuery("SELECT oia FROM org.broadleafcommerce.core.order.domain.DiscreteOrderItem doi "
                + "JOIN doi.orderItemAttributeMap oia "
                + "WHERE doi.sku = :sku "
                + "ORDER BY oia.value");
        query.setParameter("sku", sku);
        query.setHint(QueryHints.HINT_CACHEABLE, true);
        query.setHint(QueryHints.HINT_CACHE_REGION, "query.OrderItem");
        return query.getResultList();
    }

    @Override
    public List<FSReferralCustomerOrderItemXref> readReferralOrderItemsByCustomer(Customer customer) {
        // Showing everything if referralStatus not assigned
        // In case any status need to be filtered out, just not put it here.
        return readReferralOrderItemsByCustomer(customer, EnumSet.of(
            FSReferralStatusEnum.PENDDING, FSReferralStatusEnum.ADDED, FSReferralStatusEnum.CONFIRMED,
            FSReferralStatusEnum.REBATED, FSReferralStatusEnum.CANCELED));
    }

    @Override
    public List<FSReferralCustomerOrderItemXref> readReferralOrderItemsByCustomer(Customer customer, EnumSet<FSReferralStatusEnum> statusEnums) {
        TypedQuery<FSReferralCustomerOrderItemXref> query = em.createQuery(
            "SELECT rcoi FROM com.faxsun.profile.core.domain.FSReferralCustomerOrderItemXref rcoi "
                + "WHERE rcoi.customer = :customer "
                + "AND rcoi.referralStatus IN :referralStatusEnums "
                + "ORDER BY rcoi.id", FSReferralCustomerOrderItemXref.class);
        query.setParameter("customer", customer);
        query.setParameter("referralStatusEnums", statusEnums);
        query.setHint(QueryHints.HINT_CACHE_REGION, "query.OrderItem");
        return query.getResultList();
    }

    @Override
    public Map<Long, Long> readOrderItemQuantitiesByProductsAndStatus(List<Product> products, List<String> orderStatuses) {
        TypedQuery<Map> query = em.createQuery("SELECT new Map(oi.product.id AS PRODUCT_ID, SUM(oi.quantity) AS QUANTITY) " +
            "FROM org.broadleafcommerce.core.order.domain.OrderItem oi " +
            "WHERE oi.product IN :products AND oi.order.status IN :orderStatuses " +
            "GROUP BY oi.product.id", Map.class);
        query.setParameter("products", products);
        query.setParameter("orderStatuses", orderStatuses);
        query.setHint(QueryHints.HINT_CACHE_REGION, "query.Catalog");

        Map<Long, Long> resultMap = new HashMap<Long, Long>();
        for (Map queryMap : query.getResultList()) {
            resultMap.put((Long) queryMap.get("PRODUCT_ID"), (Long) queryMap.get("QUANTITY"));
        }
        return resultMap;
    }

}
